#define NOMINMAX
#include <windows.h>

#include <SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <NxPhysics.h>

int cubeId = 1;

NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxActor* groundPlane; 
//NxActor* box; 

NxReal myTimestep = 1.0f/60.0f;

void makeCube(float l)
{
    glNewList(cubeId, GL_COMPILE);
    glBegin(GL_QUADS);
    // +z front
    glColor3f(1,0,0);
    glVertex3f(-l/2, l/2, l/2);
    glVertex3f( l/2, l/2, l/2);
    glVertex3f( l/2,-l/2, l/2);
    glVertex3f(-l/2,-l/2, l/2);

    // +z back
    glColor3f(0,1,0);
    glVertex3f(-l/2, l/2,-l/2);
    glVertex3f( l/2, l/2,-l/2);
    glVertex3f( l/2,-l/2,-l/2);
    glVertex3f(-l/2,-l/2,-l/2);

    // +y top
    glColor3f(0,0,1);
    glVertex3f(-l/2, l/2, l/2);
    glVertex3f( l/2, l/2, l/2);
    glVertex3f( l/2, l/2,-l/2);
    glVertex3f(-l/2, l/2,-l/2);

    // -y bottom
    glColor3f(1,1,0);
    glVertex3f(-l/2,-l/2, l/2);
    glVertex3f( l/2,-l/2, l/2);
    glVertex3f( l/2,-l/2,-l/2);
    glVertex3f(-l/2,-l/2,-l/2);

    // +x right
    glColor3f(1,0,1);
    glVertex3f( l/2, l/2, l/2);
    glVertex3f( l/2,-l/2, l/2);
    glVertex3f( l/2,-l/2,-l/2);
    glVertex3f( l/2, l/2,-l/2);

    // -x left
    glColor3f(0,1,1);
    glVertex3f(-l/2, l/2, l/2);
    glVertex3f(-l/2,-l/2, l/2);
    glVertex3f(-l/2,-l/2,-l/2);
    glVertex3f(-l/2, l/2,-l/2);
    glEnd();
    glEndList();
}

void initGL()
{
    glClearColor(0.7, 0.7, 0.7, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, 800.0/600.0, 1.0, 500.0);
    glMatrixMode(GL_MODELVIEW);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_FOG);
    glFogi(GL_FOG_MODE, GL_LINEAR);
    glFogf(GL_FOG_START, 10.0);
    glFogf(GL_FOG_END, 40.0);
    float color[] = {0.7, 0.7, 0.7, 1.0};
    glFogfv(GL_FOG_COLOR, color);
}

void drawActors()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    int actorCount = gScene->getNbActors(); 
    NxActor** actors = gScene->getActors(); 
    while (actorCount--) 
    { 
        NxActor* actor = *actors++; 
        NxShape* const* shapes = actor->getShapes(); 
        NxU32 nShapes = actor->getNbShapes(); 
        while (nShapes--) 
        { 
            NxShape * shape = shapes[nShapes];
            int type = shape->getType();
            switch(type) 
            {          
                case NX_SHAPE_BOX:
                    {
                        NxMat34 pose = actor->getGlobalPose(); 
                        float mat[16];
                        pose.getColumnMajor44(mat);

                        glPushMatrix(); 
                        glMultMatrixf(mat);
                        glCallList(cubeId);
                        glPopMatrix();
                    }
                    break;
            } 
        } 
    } 

    glPushMatrix(); 
    glTranslatef(0.0, 0.0, -10.0);
    glColor3f(0.2, 0.2, 0.2);
    for (int i = -30; i < 30; i++)
    {
        glBegin(GL_LINES);
        glVertex3f((float)i, -2.0, -30.0);
        glVertex3f((float)i, -2.0, 30.0);
        glVertex3f(-30.0,-2.0, (float)i);
        glVertex3f(30.0, -2.0, (float)i);
        glEnd();
    }
    glPopMatrix();
}

int main(int argc, char **argv)
{
    gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
    NxSceneDesc sceneDesc;
    sceneDesc.gravity.set(0.0f, -9.8f, 0.0f); 

    gScene = gPhysicsSDK->createScene(sceneDesc);

    NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
    defaultMaterial->setRestitution(0.5);
    defaultMaterial->setStaticFriction(0.5);
    defaultMaterial->setDynamicFriction(0.5);

    gScene->setTiming(myTimestep / 4.0f, 4, NX_TIMESTEP_FIXED);

    NxPlaneShapeDesc planeDesc;
    planeDesc.d = -2;
    NxActorDesc actorDesc;
    actorDesc.shapes.pushBack(&planeDesc);
    groundPlane = gScene->createActor(actorDesc);

    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_Surface * screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE | SDL_OPENGL);
    bool running = true;
    Uint32 start;
    SDL_Event evt;

    initGL();
    makeCube(1.0f);

    while(running)
    {
        start = SDL_GetTicks();
        while(SDL_PollEvent(&evt))
        {
            switch(evt.type)
            {
                case SDL_QUIT:
                    running = false;
                    break;
                case SDL_KEYDOWN:
                    switch(evt.key.keysym.sym)
                    {
                        case SDLK_ESCAPE:
                            running = false;
                            break;
                        case SDLK_UP:
                            {
                                NxVec3 p(0,0,-500);
                                //box->addForce(p);
                                break;
                            }
                        case SDLK_LEFT:
                            {
                                NxBodyDesc bodyDesc;
                                bodyDesc.angularDamping = 0.75f;
                                bodyDesc.linearVelocity = NxVec3(0,0,0);
                                NxBoxShapeDesc boxDesc;
                                boxDesc.dimensions = NxVec3(0.5, 0.5, 0.5);
                                NxActorDesc actorDesc2;
                                actorDesc2.shapes.pushBack(&boxDesc);
                                actorDesc2.body   = &bodyDesc;
                                actorDesc2.density  = 1.0f;
                                actorDesc2.globalPose.t  = NxVec3(0,5,-10);
                                NxActor * box = gScene->createActor(actorDesc2);
                                NxVec3 p(0,5,-10);
                                box->addForce(p);
                                break;
                            }
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
        }

        gScene->simulate(myTimestep);        
        gScene->flushStream(); 

        while(!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false) )     
        {       
            // do somthing 
        }

        drawActors();

        SDL_GL_SwapBuffers();

        if (1000/30 > SDL_GetTicks() - start)
        {
            SDL_Delay(1000/30 - (SDL_GetTicks() - start));
        }
    }
    SDL_Quit();

    return 0;
}
